#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <linux/ioctl.h>
#include <sys/ioctl.h>
#include <linux/serial.h>
#include <asm-generic/ioctls.h> /* TIOCGRS485 + TIOCSRS485 ioctl definitions */
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>

typedef enum {DISABLE = 0, ENABLE} RS485_ENABLE_t;

/**
 * @brief: set the properties of serial port
 * @Param: fd: file descriptor
 * @Param: nSpeed: Baud Rate
 * @Param: nBits: character size
 * @Param: nEvent: parity of serial port
 * @Param: nStop: stop bits
 */
int set_port(int fd, int nSpeed, int nBits, char nEvent, int nStop)
{
	struct termios newtio;


	memset(&newtio, 0, sizeof(newtio));
	/* ignore modem control lines and enable receiver */
	newtio.c_cflag = newtio.c_cflag |= CLOCAL | CREAD;
	newtio.c_cflag &= ~CSIZE;
	/* set character size */
	switch (nBits) {
		case 8:
			newtio.c_cflag |= CS8;
			break;
		case 7:
			newtio.c_cflag |= CS7;
			break;
		case 6:
			newtio.c_cflag |= CS6;
			break;
		case 5:
			newtio.c_cflag |= CS5;
			break;
		default:
			newtio.c_cflag |= CS8;
			break;
	}
	/* set the parity */
	switch (nEvent) {
		case 'o':
		case 'O':
			newtio.c_cflag |= PARENB;
			newtio.c_cflag |= PARODD;
			newtio.c_iflag |= (INPCK | ISTRIP);
			break;
		case 'e':
		case 'E':
			newtio.c_cflag |= PARENB;
			newtio.c_cflag &= ~PARODD;
			newtio.c_iflag |= (INPCK | ISTRIP);
			break;
		case 'n':
		case 'N':
			newtio.c_cflag &= ~PARENB;
			break;
		default:
			newtio.c_cflag &= ~PARENB;
			break;
	}
	/* set the stop bits */
	switch (nStop) {
		case 1:
			newtio.c_cflag &= ~CSTOPB;
			break;
		case 2:
			newtio.c_cflag |= CSTOPB;
			break;
		default:
			newtio.c_cflag &= ~CSTOPB;
			break;
	}
	/* set output and input baud rate */
	switch (nSpeed) {
		case 0:
			cfsetospeed(&newtio, B0);
			cfsetispeed(&newtio, B0);
			break;
		case 50:
			cfsetospeed(&newtio, B50);
			cfsetispeed(&newtio, B50);
			break;
		case 75:
			cfsetospeed(&newtio, B75);
			cfsetispeed(&newtio, B75);
			break;
		case 110:
			cfsetospeed(&newtio, B110);
			cfsetispeed(&newtio, B110);
			break;
		case 134:
			cfsetospeed(&newtio, B134);
			cfsetispeed(&newtio, B134);
			break;
		case 150:
			cfsetospeed(&newtio, B150);
			cfsetispeed(&newtio, B150);
			break;
		case 200:
			cfsetospeed(&newtio, B200);
			cfsetispeed(&newtio, B200);
			break;
		case 300:
			cfsetospeed(&newtio, B300);
			cfsetispeed(&newtio, B300);
			break;
		case 600:
			cfsetospeed(&newtio, B600);
			cfsetispeed(&newtio, B600);
			break;
		case 1200:
			cfsetospeed(&newtio, B1200);
			cfsetispeed(&newtio, B1200);
			break;
		case 1800:
			cfsetospeed(&newtio, B1800);
			cfsetispeed(&newtio, B1800);
			break;
		case 2400:
			cfsetospeed(&newtio, B2400);
			cfsetispeed(&newtio, B2400);
			break;
		case 4800:
			cfsetospeed(&newtio, B4800);
			cfsetispeed(&newtio, B4800);
			break;
		case 9600:
			cfsetospeed(&newtio, B9600);
			cfsetispeed(&newtio, B9600);
			break;
		case 19200:
			cfsetospeed(&newtio, B19200);
			cfsetispeed(&newtio, B19200);
			break;
		case 38400:
			cfsetospeed(&newtio, B38400);
			cfsetispeed(&newtio, B38400);
			break;
		case 57600:
			cfsetospeed(&newtio, B57600);
			cfsetispeed(&newtio, B57600);
			break;
		case 115200:
			cfsetospeed(&newtio, B115200);
			cfsetispeed(&newtio, B115200);
			break;
		case 230400:
			cfsetospeed(&newtio, B230400);
			cfsetispeed(&newtio, B230400);
			break;
		default:
			cfsetospeed(&newtio, B115200);
			cfsetispeed(&newtio, B115200);
			break;
	}
	/* set timeout in deciseconds for non-canonical read */
	newtio.c_cc[VTIME] = 0;
	/* set minimum number of characters for non-canonical read */
	newtio.c_cc[VMIN] = 0;
	/* flushes data received but not read */
	tcflush(fd, TCIFLUSH);
	/* set the parameters associated with the terminal from
		the termios structure and the change occurs immediately */
	if((tcsetattr(fd, TCSANOW, &newtio))!=0)
	{
		perror("set_port/tcsetattr");
		return -1;
	}

	return 0;
}

/******************************************************************************
 * NAME:
 * read_data
 *
 * DESCRIPTION:
 * read data to serial port
 *
 *
 * PARAMETERS:
 * fd - The fd of serial port to write
 * *buf - The buffer to keep data
 * len - The count of data
 * time - time out
 *
 * RETURN:
 * Count of data wrote
 ******************************************************************************/
int read_data(int fd, char *buf, int len, int time_ms)
{
	int i;
	int nfds;
	int nread = 0;
	unsigned char read_byte = 0;
	char read_temp[5120] = {0};
	char read_dmx[514] = {0};
	fd_set readfds;
	struct timeval tv;
	char *p;

	//printf("***read com data***\n");

	tv.tv_sec = time_ms/1000;
	tv.tv_usec = time_ms%1000*1000;
	FD_ZERO(&readfds);
	FD_SET(fd, &readfds);
	bzero(read_temp, sizeof(read_temp));
	nfds = select(fd+1, &readfds, NULL, NULL, &tv);

#if 1
	if(nfds == 0)
	{
		printf("timeout!\r\n");
		return -1;
	}else{
		bzero(read_dmx, sizeof(read_dmx));
		nread = read(fd, read_dmx, sizeof(read_dmx));
		if(nread<=0){
			printf("read get problem[%d]!\r\n", nread);
			return -1;
		}
		printf("nread: %d\n", nread);
		//printf("recv dmx data: %s\n", read_dmx);
		//printf("[000]%02x", read_dmx[0]);
		//for(i = 1; i < sizeof(read_dmx); i ++)
		//{
		//	printf("[%03d]%02x", i, read_dmx[i]);
		//	//printf("%x", i, read_dmx[i]);
		//	if(i%10 == 0)
		//		printf("\n");
		//}
		//printf("\n");
		//strncpy(buf, read_dmx, strlen(read_dmx));
		memcpy(buf, read_dmx, sizeof(read_dmx));
	}

#else
	if(nfds == 0)
	{
		printf("timeout!\r\n");
		return -1;
	}else{
		nread = read(fd, &read_byte, sizeof(char));//
		if(nread<=0){
			printf("read get problem[%d]!\r\n", nread);
			return -1;
		}
		printf("recv data: %c\n", read_byte);
		if(read_byte == '<'){
			//printf("data start...\n");
			p = read_temp;
			strcpy(p, &read_byte);
			*p++;
			while(1)
			{
				nread = read(fd, &read_byte, sizeof(char));//
				if(nread<=0){
					printf("read get problem[%d]!\r\n", nread);
					return -1;
				}
				printf("recv data: %c\n", read_byte);

				if(read_byte == '>'){
					//printf("data over...\n");
					strcpy(p, &read_byte);
					break;
				}
				strcpy(p, &read_byte);
				*p++;
			}
			printf("recv data: %s\n", read_temp);
			//memcpy(buf, read_temp, strlen(read_temp));
			strncpy(buf, read_temp, strlen(read_temp));
			return 0;
		}else{
			printf("data start error!\n");
			return -1;
		}
	}
#endif

}


// 设置为特殊波特率，比如200000
int set_speci_baud(int fd, int baud)
{
        struct serial_struct ss, ss_set;
        struct termios Opt;

        tcgetattr(fd, &Opt);
        cfsetispeed(&Opt, B38400);
        cfsetospeed(&Opt, B38400);
        tcflush(fd, TCIFLUSH);/*handle unrecevie char*/
        tcsetattr(fd, TCSANOW, &Opt);

        if((ioctl(fd, TIOCGSERIAL, &ss)) < 0)
        {
			printf("BAUD: error to get the serial_struct info:%s\n", strerror(errno));
            return -1;
        }
		printf("ss.line: %d, ss.custom_divisor: %d, ss.baud_base: %d, ss.irq: %d\n", ss.line, ss.custom_divisor, ss.baud_base, ss.irq);

        ss.flags = ASYNC_SPD_CUST;
        ss.custom_divisor = ss.baud_base/baud;
        if((ioctl(fd, TIOCSSERIAL, &ss)) < 0)
        {
			printf("BAUD: error to set serial_struct:%s\n", strerror(errno));
            return -2;
        }

        ioctl(fd, TIOCGSERIAL, &ss_set);
        printf("BAUD: success set baud to %d,custom_divisor=%d,baud_base=%d\n",baud, ss_set.custom_divisor, ss_set.baud_base);

        return 0;
}

int rs485_enable(const int fd, const RS485_ENABLE_t enable)
{
        struct serial_rs485 rs485conf;
        int res;

        /* Get configure from device */
        res = ioctl(fd, TIOCGRS485, &rs485conf);
        if (res < 0) {
                perror("Ioctl error on getting 485 configure:");
                close(fd);
                return res;
        }

        /* Set enable/disable to configure */
        if (enable) {   // Enable rs485 mode
                rs485conf.flags |= SER_RS485_ENABLED;
        } else {        // Disable rs485 mode
                rs485conf.flags &= ~(SER_RS485_ENABLED);
        }

        rs485conf.delay_rts_before_send = 0x00000004;

        /* Set configure to device */
        res = ioctl(fd, TIOCSRS485, &rs485conf);
        if (res < 0) {
                perror("Ioctl error on setting 485 configure:");
                close(fd);
        }

        return res;
}

int runDmxTest(void)
{
	int i;
	int ret;
	int fd, fd_gpio;
	char w_buf[5] = {0x1, 0x2, 0x3};
	char r_buf[514] = {0xa};
	int channel = 1;

	fd  = open("/dev/ttymxc4", O_RDWR);
	if(fd < 0){
		printf("open dev error!\n");
		return -1;
	}

	i = set_port(fd, 38400, 8, 'N', 2);
	ret = set_speci_baud(fd, 250000);

	rs485_enable(fd,ENABLE);
	while(1)
	{
		ret = read(fd, r_buf, sizeof(r_buf));
		printf("ret: %d\n",ret);
		usleep(100*1000);
	}

	//while(1)
	//{
	//	bzero(r_buf, sizeof(r_buf));
	//	ret = read_data(fd, r_buf, strlen(r_buf), 100);
	//	printf("[000]%02x", r_buf[0]);
	//	printf("%02x", r_buf[0]);
	//	for(i = 1; i < sizeof(r_buf); i ++)
	//	{
	//		//printf("[%03d]%02x", i, r_buf[i]);
	//		printf("%02x", r_buf[i]);
	//		if(i%10 == 0)
	//			printf("\n");
	//	}
	//	printf("\n");
	//	//usleep(100*1000);
	//}

	close(fd);
	return 0;
}
